//! State transition types

use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs};
use solana_program::{
    program_error::ProgramError,
    msg,
    program_pack::{IsInitialized, Pack, Sealed},
};
use std::ops::{Mul, Sub, Div, Add};

/// Program states.
#[repr(C)]
#[derive(Debug, Default, PartialEq)]
pub struct RewardV1 { // USDT 单币质押池
    /// 最后一次更新时间 用于计算当前应当产多少farm
    pub last_update: u64,
    /// 每plot 大约583ms 一个 plot 奖励farm数量
    pub reward_per_second: u64,
    /// 每个份额所能分的farm数量
    pub reward_per_share: u64,
    /// 总质押量
    pub total_supply: u64,
}

impl RewardV1 {
    /// update_pool 更新矿池信息 totalSupply => 总质押量 slot => 当前时间点
    pub fn update_pool(&mut self, slot:u64) {
        msg!("update pool slot : {0}",slot);
        if slot <= self.last_update {
            return;
        }
        if self.total_supply == 0 {
            return;
        } else{
            // 当前farm应该产出量 每秒产出 * 时间间隔
            let farm_reward:u64 = self.reward_per_second.mul(slot.sub(self.last_update));
            // 时间加权 份额 挖矿
            self.reward_per_share = self.reward_per_share.add(farm_reward.div(self.total_supply));
        }
        self.last_update = slot;
    }
}

impl Sealed for RewardV1 {}
impl IsInitialized for RewardV1 { // 对象初始化
    fn is_initialized(&self) -> bool {
        true
    }
}

impl Pack for RewardV1 {
    const LEN: usize = 33;

    fn pack_into_slice(&self, output: &mut [u8]) {
        let output = array_mut_ref![output, 0, 32];
        let (
            last_update,
            reward_per_second,
            reward_per_share,
            total_supply,
        ) = mut_array_refs![output, 8,8,8,8];
        *last_update = self.last_update.to_le_bytes();
        *reward_per_second = self.reward_per_second.to_le_bytes();
        *reward_per_share = self.reward_per_share.to_le_bytes();
        *total_supply = self.total_supply.to_le_bytes();
    }

    /// Unpacks a byte buffer into a [RewardV1](struct.RewardV1.html).
    fn unpack_from_slice(input: &[u8]) -> Result<Self, ProgramError> {
        let input = array_ref![input, 0, 32];
        #[allow(clippy::ptr_offset_with_cast)]
            let (
            last_update,
            reward_per_second,
            reward_per_share,
            total_supply,
        ) = array_refs![input,8,8,8,8];
        Ok(Self {
            last_update:u64::from_le_bytes(*last_update),
            reward_per_second:u64::from_le_bytes(*reward_per_second),
            reward_per_share:u64::from_le_bytes(*reward_per_share),
            total_supply:u64::from_le_bytes(*total_supply),
        })
    }
}